package com.mjlf.framework.aop;

import com.mjlf.framework.annotation.aop.After;
import com.mjlf.framework.annotation.aop.Aspect;
import com.mjlf.framework.annotation.aop.Before;
import com.mjlf.framework.util.BeanManager;
import com.mjlf.framework.util.ClassManager;
import org.apache.commons.collections.map.HashedMap;
import org.junit.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @auther a123
 * @create 2018-08-29 17:44
 * @desc 增强方法管理
 */
public final class AdviseMethodManager {

    public static Map<Annotation, AdviseMethod> PROXY_METHOD = new HashedMap();

    public static void initAdviseMethod(){
        ClassManager.getClassSet(Aspect.class).stream().forEach(cls ->{
            List<Method> methods = Arrays.asList(cls.getDeclaredMethods());
            if(methods != null){
                methods.stream().filter(method -> {
                    if(method.isAnnotationPresent(Before.class) ||
                            method.isAnnotationPresent(After.class)){
                        return true;
                    }
                    return false;
                }).forEach(method -> {
                    if(method.isAnnotationPresent(Before.class)){//前置增强
                        Before before = method.getAnnotation(Before.class);

                        Parameter[] parameters = method.getParameters();
                        Class<?>[] argsType = new Class<?>[parameters.length];
                        String[] argsName = new String[parameters.length];
                        for(int i = 0; i < parameters.length; i++){
                            Parameter parameter = parameters[i];
                            argsType[i] = parameter.getType();
                            argsName[i] = parameter.getName();
                        }

                        try {
                            AdviseMethod proxyMethod = new AdviseMethod(before, method, BeanManager.getBean(cls));
                            proxyMethod.setArgsType(argsType);
                            proxyMethod.setArgsName(argsName);
                            PROXY_METHOD.put(before, proxyMethod);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }else if(method.isAnnotationPresent(After.class)){
                        After after = method.getAnnotation(After.class);

                        Class<?> returnType = after.returnType();
                        String returnName = after.returnName();

                        Parameter[] parameters = method.getParameters();
                        Class<?>[] argsType = new Class<?>[parameters.length];
                        String[] argsName = new String[parameters.length];

                        boolean hasReturn = false;//用于验证再参数中是否含有被带来的方法的返回值
                        boolean isOk = true;       //如果需要再增强中包含返回值，这边值用于验证是否包含该参数
                        if(returnType != After.class){
                            hasReturn = true;
                            isOk = false;
                        }

                        for(int i = 0; i < parameters.length; i++){
                            Parameter parameter = parameters[i];
                            argsType[i] = parameter.getType();
                            argsName[i] = parameter.getName();
                            if(hasReturn){
                                if(parameter.getType() == returnType &&
                                    parameter.getName() == returnName){
                                    isOk = true;
                                }
                            }
                        }

                        if(!isOk){
                            throw new RuntimeException("后置增强中存在错误，参数中不包含被代理返回返回值:" + method.getName());
                        }

                        try {
                            AdviseMethod proxyMethod = new AdviseMethod(after, method, BeanManager.getBean(cls));
                            proxyMethod.setArgsType(argsType);
                            proxyMethod.setArgsName(argsName);

                            if(returnType != After.class){
                                proxyMethod.setReturnType(returnType);
                                proxyMethod.setReturnName(returnName);
                            }
                            PROXY_METHOD.put(after, proxyMethod);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        });
    }

    public static AdviseMethod getProxyMethodByName(String name){
        return PROXY_METHOD.get(name);
    }
}
